package com.hmall.serialize.Kryo;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.hmall.serialize.Serializer;
import com.hmall.remoting.dto.RpcRequest;
import com.hmall.remoting.dto.RpcResponse;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

@Slf4j
public class KryoSerializer implements Serializer {

    public ThreadLocal<Kryo> kryoThreadLocal = new ThreadLocal<Kryo>().withInitial(
            () -> {
                Kryo kryo = new Kryo();
                kryo.register(RpcRequest.class);
                kryo.register(RpcResponse.class);
                return kryo;
            }
    );

    @Override
    public byte[] serialize(Object obj) {
        try (ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
             Output output = new Output(arrayOutputStream)) {
            Kryo kryo = kryoThreadLocal.get();
            kryo.writeObject(output, obj);
            output.flush();
            return arrayOutputStream.toByteArray();
        } catch (Exception e) {
            log.error("Serialization failed", e);
            throw new SerializeException("Serialization failed", e);
        }
    }

    @Override
    public <T> T deserialize(byte[] bytes, Class<T> clazz) {
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
             Input input = new Input(byteArrayInputStream)) {
            Kryo kryo = kryoThreadLocal.get();
            // byte->Object:从byte数组中反序列化出对对象
            return kryo.readObject(input, clazz);
        } catch (Exception e) {
            log.error("Deserialization failed", e);
            throw new SerializeException("Deserialization failed", e);
        }
    }

    public class SerializeException extends RuntimeException {
        public SerializeException(String message) {
            super(message);
        }

        public SerializeException(String message, Throwable cause) {
            super(message, cause);
        }
    }
}
